#include "paging.h"

#define P 1u
#define RW 2u
#define US 4u
#define A 32u
#define D 64u
#define AVL 512u
#define ADD 4096u


int paging();
void disable_paging();
void enable_paging();

char bit_map[PAGE_CANT/8];

void clearPage(unsigned int page){
        char bit = page%8;
        bit_map[page/8] &= ~(1 << bit);
        return;
}

int pagePresent(unsigned int page){
        char bit = page%8;
        return bit_map[page/8] & (1<<bit);
}

void setPage(unsigned int page){
        char bit = page%8;
        bit_map[page/8] |= 1 << bit;
        return;
}

unsigned int getFreePage(){
        unsigned int i;
        for(i=0x401 ; i<PAGE_CANT ; i++){
                if(!pagePresent(i)){
			setPage(i);
                        return i;
		}
	}
	printk("out of memory!");
	while(1);
}



unsigned int setP(unsigned int * entry ,unsigned int p){
	*entry  &= ~P;
	*entry  |= (P * p);
}

int getP(unsigned int * entry){
	return (*entry & P);
}

unsigned int setRW(unsigned int * entry ,unsigned int rw){
	*entry  &= ~RW;
	*entry  |= (RW * rw);
}

unsigned int setUS(unsigned int * entry ,unsigned int us){
	*entry  &= ~US;
	*entry  |= (US * us);
}

unsigned int setA(unsigned int * entry ,unsigned int a){
	*entry  &= ~A;
	*entry  |= (A * a);
}


unsigned int setD(unsigned int * entry ,unsigned int d){
	*entry  &= ~D;
	*entry  |= (D * d);
}

int getD(unsigned int * entry){
	return (*entry & D);
}

unsigned int setAVL(unsigned int * entry ,unsigned int avl){
	*entry  &= ~AVL;
	*entry  &= ~(AVL * 2);
	*entry  &= ~(AVL * 4);
	*entry  |= (AVL * avl);
}

unsigned int setADD(unsigned int * entry ,unsigned int add){
	*entry  &= 0xFFF;
	*entry  |= (ADD * add);
}

unsigned int getADD(unsigned int * entry){
	return (*entry >> 12);
}

void setDefoultEntry(unsigned int * entry, unsigned int addres){
	*entry=0;
	setP(entry,1);
	setRW(entry,1);
	setUS(entry,1);
	setA(entry,0);
	setD(entry,0);
	setAVL(entry,0);
	setADD(entry,addres);
}

void open_page(unsigned int page){
	unsigned int* dd_table=(unsigned int *)DD_TABLE;
	
	setDefoultEntry(dd_table+1,page);
}

void new_heap(){
	unsigned int * page_directory = (unsigned int *)PAGE_DIRECTORY;
	
	unsigned int table_page=getFreePage();
	open_page(table_page);
	unsigned int * page_table = (unsigned int *)PAGE_TABLE;
	
	setDefoultEntry(page_table,getFreePage());
	
	unsigned int j;
	for(j=1 ; j <  1023u ; j++){
		setP((page_table + j),0);
	}
	
	setDefoultEntry((page_directory + 3u),table_page);

}
/*void new_heap(){
		unsigned int flag= paging();
	if(flag){
		disable_paging();
	}
	
	unsigned int * page_table = (unsigned int *)(getFreePage()*PAGESIZE);
	
	setDefoultEntry(page_table,getFreePage());
	
	unsigned int j;
	for(j=1 ; j <  PAGE_ENTRYS ; j++){
		setP((page_table + j),0);
	}
	
	if(flag){
		enable_paging();
	}

}*/

void new_stack(){
	unsigned int * page_directory = (unsigned int *)PAGE_DIRECTORY;
	
	unsigned int table_page=getFreePage();
	open_page(table_page);
	unsigned int * page_table = (unsigned int *)PAGE_TABLE;
	
	setDefoultEntry((page_table + 1023u),getFreePage());
	setDefoultEntry((page_table + 1022u),getFreePage());
	setDefoultEntry((page_table + 1021u),getFreePage());
	setDefoultEntry((page_table + 1020u),getFreePage());
	setDefoultEntry((page_table + 1019u),getFreePage());
	setDefoultEntry((page_table + 1018u),getFreePage());
	
	unsigned int j;
	for(j=0 ; j <  1018u ; j++){
		setP((page_table + j),0);
	}
	
	setDefoultEntry((page_directory + 1023u),table_page);

}

void grow_stack(){

	int i,j;
	unsigned int vm;
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table= (unsigned int *)PAGE_TABLE;


	do{
		int i=PAGE_ENTRYS-1;
		if(!getP(page_directory+i)){
			new_stack();
			return;
		}
	
		while(getP(page_directory+i)){
			i--;
		}
		i++;
	
		unsigned int table_page=getADD(page_directory+i);
	
		int j=PAGE_ENTRYS-1;
		open_page(table_page);
		while(getP(page_table+j)){
			j--;
		}
	

		vm=((i << 22) & 0xFFC00000) | ((j << 12) & 0x3FF000);

		if(vm > (get_stack() - PAGESIZE * 5) ){
			if(j<0){
				i--;
				table_page=getFreePage();
				setDefoultEntry((page_directory + i),table_page);
				open_page(table_page);
				page_table = (unsigned int *)PAGE_TABLE;
			}

			setDefoultEntry((page_table + j),getFreePage());
		}
		
	}while(vm > (get_stack() - PAGESIZE * 5) );

}

unsigned int grow_heap(){

	int i,j;

	unsigned int table_page=0;
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table=(unsigned int *)PAGE_TABLE;

	i=3;
	if(!getP(page_directory+i)){
		new_heap();
		return 0x00C00000;
	}
	
	while(getP(page_directory+i)){
		i++;
	}
	i--;
	
	table_page=getADD(page_directory+i);
	
	j=0;
	open_page(table_page);
	while(getP(page_table+j)){
		j++;
	}

	if(j>=PAGE_ENTRYS){
		i++;
		table_page=getFreePage();
		setDefoultEntry((page_directory + i),table_page);
		open_page(table_page);
		page_table = (unsigned int *)PAGE_TABLE;
		j=0;
	}

	setDefoultEntry((page_table + j),getFreePage());
	
	return ((i << 22) & 0xFFC00000) | ((j << 12) & 0x3FF000);

}


unsigned int stack_limit(){
	int i,j;

	unsigned int table_page=0;
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table= (unsigned int *)PAGE_TABLE;

	i=PAGE_ENTRYS-1;
	if(!getP(page_directory+i)){
		return 0xFFFFFFFF;
	}
	
	while(getP(page_directory+i)){
		i--;
	}
	i++;
	
	table_page=getADD(page_directory+i);
	
	j=PAGE_ENTRYS-1;
	open_page(table_page);
	while(getP(page_table+j)){
		j--;
	}
	

	return ((i << 22) & 0xFFC00000) | ((j << 12) & 0x3FF000);
}

unsigned int heap_limit(){
	int i,j;

	unsigned int table_page=0;
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table= (unsigned int *)PAGE_TABLE;

	i=3;
	if(!getP(page_directory+i)){
		return 0x00C00000;
	}
	
	while(getP(page_directory+i)){
		i++;
	}
	i--;
	
	table_page=getADD(page_directory+i);
	
	j=0;
	open_page(table_page);
	while(getP(page_table+j)){
		j++;
	}
	

	return ((i << 22) & 0xFFC00000) | ((j << 12) & 0x3FF000);
}

void free_heap(){
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table= (unsigned int *)PAGE_TABLE;

	int i=3;	
	while(getP(page_directory+i)){
		unsigned int table_page=getADD(page_directory+i);
	
		int j=0;
		open_page(table_page);
		while(getP(page_table+j)){
			unsigned int page=getADD(page_table+j);
			clearPage(page);
			j++;
		}
		
		clearPage(table_page);
		i++;
	}
}

void free_stack(){
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	unsigned int * page_table= (unsigned int *)PAGE_TABLE;

	int i=PAGE_ENTRYS-1;
	while(getP(page_directory+i)){
		unsigned int table_page=getADD(page_directory+i);
	
		int j=PAGE_ENTRYS-1;
		open_page(table_page);
		while(getP(page_table+j)){
			unsigned int page=getADD(page_table+j);
			clearPage(page);
			j--;
		}
		
		clearPage(table_page);
		i--;
	}
}



int isDirty(unsigned int page_table_num,unsigned int entry){
	unsigned int* page_directory=(unsigned int *)PAGE_DIRECTORY;
	
	unsigned int table_page=getADD(page_directory+page_table_num);
	open_page(table_page);
	unsigned int* page_table = (unsigned int *)PAGE_TABLE;
	
	
	int ret=getD(page_table + entry);
	setD(page_table + entry,0);
	return ret;
}

void init_page_directory(unsigned int page_num){
	unsigned int i=0;
	unsigned int * page_directory = (unsigned int *)(page_num*PAGESIZE);
	
	setDefoultEntry((page_directory + i++),KERNEL_PAGETABLE);
	
	setDefoultEntry((page_directory + i++),CACHE_PAGETABLE);
	
	unsigned int dd_page=getFreePage();
	unsigned int * page_table = (unsigned int *)(dd_page*PAGESIZE);
	unsigned int j;
	for(j=0 ; j <  PAGE_ENTRYS ; j++){
		setP((page_table + j),0);
	}
	setDefoultEntry((page_table+0),page_num);
	setDefoultEntry((page_table+2),dd_page);
	
	setDefoultEntry((page_directory + i++),dd_page);
	
	
	for( ; i < PAGE_ENTRYS ; i++){
		setP((page_directory + i),0);
	}
}

unsigned int new_page_dir(){
	unsigned int pagedir=getFreePage();
	unsigned int flag= paging();
	if(flag){
		disable_paging();
	}
	
	init_page_directory(pagedir);
	
	if(flag){
		enable_paging();
	}
	
	return pagedir;
}

void init_kernel_page_table(){
	unsigned int i;
	unsigned int * page_table= (unsigned int *)(KERNEL_PAGETABLE*PAGESIZE);
	
	for(i=0 ; i < PAGE_ENTRYS ; i++){
		
		setDefoultEntry((page_table + i),i);
		
		setPage(i);
	}
	
	setPage(KERNEL_PAGETABLE);
}

void init_cache_page_table(){

	unsigned int i;
	unsigned int * page_table = (unsigned int *)(CACHE_PAGETABLE*PAGESIZE);
	
	setDefoultEntry(page_table,getFreePage());
	
	for(i=0 ; i < PAGE_ENTRYS ; i++){
		
		setDefoultEntry((page_table + i),getFreePage());
	}

	setPage(CACHE_PAGETABLE);
}

void init_paging(){

	unsigned int i;

	for(i=0;i<PAGE_ENTRYS;i++){
		setPage(i);
	}
	
	init_kernel_page_table();
	init_cache_page_table();
}

